﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Reactive;
using System.Threading;
using System.Threading.Tasks;
using DynamicData;
using Microsoft.EntityFrameworkCore;
using MilkTeaMachine.OfflineClient.EntityFrameworkCore;
using MilkTeaMachine.OfflineClient.Models;
using ReactiveUI;
using ReactiveUI.Fody.Helpers;
using Splat;

namespace MilkTeaMachine.OfflineClient.ViewModels;

public class FormulaInputViewModel : ViewModelBase, IRoutableViewModel, IScreen
{
    private readonly ApplicationDbContext _db;

    public FormulaInputViewModel(IScreen hostScreen)
    {
        HostScreen = hostScreen;
        _db = Locator.Current.GetService<ApplicationDbContext>()!;
        _totalFormulaVoObservableList = GetFormulaVoObservableList();
        FormulaVoListToken =
            new ObservableCollection<FormulaVO>(_totalFormulaVoObservableList.Skip(SkipCount).Take(TakeCount));
        EditFormulaCmd = ReactiveCommand.CreateFromTask(async (int formulaId) =>
        {
            await EditFormulaAsync(formulaId);
        });
        CopyCreateNewFormulaCmd = ReactiveCommand.CreateFromTask(async (int formulaId) =>
        {
            await CopyCreateNewFormulaAsync(formulaId);
        });
        DeleteFormulaCmd = ReactiveCommand.CreateFromTask(async (int formulaId) =>
        {
            await DeleteFormulaAsync(formulaId);
        });
        NextPageCmd = ReactiveCommand.CreateFromTask(NextPage,
            this.WhenAnyValue(
                vm => vm.SkipCount,
                vm => vm._totalFormulaVoObservableList.Count,
                (skip, total) => skip + TakeCount < total));
        PrePageCmd = ReactiveCommand.CreateFromTask(PrePage,
            this.WhenAnyValue(vm => vm.SkipCount, skip => skip > 0));
        CreateNewFormulaCmd = ReactiveCommand.CreateFromObservable(() =>
            HostScreen.Router.Navigate.Execute(new FormulaSaveOrUpdateViewModel(this, HostScreen, ActionTypeEnum.新建)));
        SaveAndQuitCmd = ReactiveCommand.CreateFromTask(SaveAndQuitAsync);
    }

    public FormulaInputViewModel(): this(default!)
    {
        
    }
    private async Task SaveAndQuitAsync()
    {
        if (_removeFormulaIdList.Count != 0)
            await _db.Formulas.Where(formula => _removeFormulaIdList.Contains(formula.Id)).ExecuteDeleteAsync();
        if (_removeFormulaIdList.Count != 0)
            await _db.FormulaIngredients.Where(ingredient => _removeFormulaIdList.Contains(ingredient.FormulaId))
                .ExecuteDeleteAsync();
        await _db.SaveChangesAsync();
        HostScreen.Router.NavigateBack.Execute().Subscribe();
    }

    private ObservableCollection<FormulaVO> GetFormulaVoObservableList()
    {
        var formulaList = _db.Formulas.ToList();
        return new ObservableCollection<FormulaVO>(formulaList.Select(formula => new FormulaVO
        {
            FormulaId = formula.Id,
            FormulaName = formula.Name,
            Code = formula.Code
        }));
    }

    private Task PrePage()
    {
        SkipCount -= TakeCount;
        FormulaVoListToken.Clear();
        FormulaVoListToken.AddRange(_totalFormulaVoObservableList.Skip(SkipCount).Take(TakeCount));
        return Task.CompletedTask;
    }


    private Task NextPage()
    {
        SkipCount += TakeCount;
        FormulaVoListToken.Clear();
        FormulaVoListToken.AddRange(_totalFormulaVoObservableList.Skip(SkipCount).Take(TakeCount));
        return Task.CompletedTask;
    }

    private Task DeleteFormulaAsync(int formulaId)
    {
        _removeFormulaIdList.Add(formulaId);
        _totalFormulaVoObservableList =
            new ObservableCollection<FormulaVO>(
                _totalFormulaVoObservableList.Where(vo => !_removeFormulaIdList.Contains(vo.FormulaId)));
        FormulaVoListToken.Clear();
        if (SkipCount == _totalFormulaVoObservableList.Count)
            SkipCount -= TakeCount;
        FormulaVoListToken.AddRange(_totalFormulaVoObservableList.Skip(SkipCount).Take(TakeCount));
        return Task.CompletedTask;
    }

    private Task CopyCreateNewFormulaAsync(int formulaId)
    {
        HostScreen.Router.Navigate.Execute(
            new FormulaSaveOrUpdateViewModel(this, HostScreen, ActionTypeEnum.复制新建, formulaId));
        return Task.CompletedTask;
    }

    private Task EditFormulaAsync(int formulaId)
    {
        HostScreen.Router.Navigate.Execute(
            new FormulaSaveOrUpdateViewModel(this, HostScreen, ActionTypeEnum.修改, formulaId));
        return Task.CompletedTask;
    }

    private const int TakeCount = 8;
    private readonly List<int> _removeFormulaIdList = new(8);
    public IScreen HostScreen { get; }
    public string UrlPathSegment => nameof(FormulaInputViewModel);
    private ObservableCollection<FormulaVO> _totalFormulaVoObservableList;
    [Reactive] private int SkipCount { get; set; }
    public ObservableCollection<FormulaVO> FormulaVoListToken { get; set; }
    public ReactiveCommand<int, Unit> EditFormulaCmd { get; }
    public ReactiveCommand<int, Unit> CopyCreateNewFormulaCmd { get; }
    public ReactiveCommand<int, Unit> DeleteFormulaCmd { get; }
    public ReactiveCommand<Unit, Unit> NextPageCmd { get; }
    public ReactiveCommand<Unit, Unit> PrePageCmd { get; }
    public RoutingState Router { get; } = new();
    public ReactiveCommand<Unit, IRoutableViewModel> CreateNewFormulaCmd { get; }
    public ReactiveCommand<Unit, Unit> SaveAndQuitCmd { get; }
}